package br.mikhas.util;

import java.util.HashMap;

/**
 * A wrapper which may be used as composed key for a {@link HashMap}
 * 
 * @author Mikhail Domanoski
 * 
 */
public class Pair<F, S> {
	/**
	 * First object of the pair
	 */
	private final F first;
	/**
	 * Second object of the pair
	 */
	private final S second;

	/**
	 * Creates a new Pair setting the objects which belongs to it
	 * 
	 * @param first
	 *            The first object of the pair
	 * @param second
	 *            The second object of the pair
	 */
	public Pair(F first, S second) {
		this.first = first;
		this.second = second;
	}

	/**
	 * Checks if the given object is in the pair
	 * 
	 * @param object
	 *            The object to be checked
	 * @return <tt>true</tt> if the object belongs to the pair
	 */
	public boolean contains(Object object) {
		if (object == null)
			return first == null || second == null;
		else
			return object.equals(first) || object.equals(second);
	}

	/**
	 * Gets the first object of the pair
	 * 
	 * @return The first object of the pair
	 */
	public F getFirst() {
		return this.first;
	}

	/**
	 * Gets the second object of the pair
	 * 
	 * @return The second object of the pair
	 */
	public S getSecond() {
		return this.second;
	}

	/**
	 * Gets the value of the other object in the pair.
	 * <p>
	 * The method checks if the pair contains the object and return the other
	 * object.
	 * <p>
	 * If the pair does not contains the provided object, the method return
	 * null;
	 * 
	 * @param value
	 *            One of the objects of the pair
	 * @return The other object on the pair
	 */
	public Object getOther(Object value) {
		if (this.contains(value)) {
			if (value == null) {
				return first == null ? second : first;
			} else {
				return value.equals(first) ? second : first;
			}
		}
		return null;
	}

	/**
	 * Cached calculated hash code
	 */
	private int hash = Integer.MIN_VALUE;

	/**
	 * Calculates the pair hashCode based on the pair elements.
	 * <p>
	 * The generated hashCode disconsider the order of the lements
	 */
	@Override
	public int hashCode() {
		if (hash == Integer.MIN_VALUE) {
			final int prime = 31;
			int result = 1;

			int hash1 = first != null ? first.hashCode() : 0;
			int hash2 = second != null ? second.hashCode() : 0;

			if (hash2 > hash1) {
				int temp = hash2;
				hash2 = hash1;
				hash1 = temp;
			}

			result = prime * result + hash1;
			result = prime * result + hash2;
			hash = result;
		}
		return hash;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;

		if (obj instanceof Pair) {
			return this.hashCode() == obj.hashCode();
		} else
			return false;
	}

}
